package tr.edu.yildiz.ce.dap.acm.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.xml.bind.JAXBException;

import tr.edu.yildiz.ce.dap.acm.Activator;
import tr.edu.yildiz.ce.dap.acm.gen.Cache;
import tr.edu.yildiz.ce.dap.acm.gen.CachedComponent;
import tr.edu.yildiz.ce.dap.acm.service.CacheManagerService;
import tr.edu.yildiz.ce.dap.acm.util.CacheProperties;
import tr.edu.yildiz.ce.dap.acm.util.CacheXmlConverter;
import tr.edu.yildiz.ce.dap.acm.util.exception.AlreadyCachedException;
import tr.edu.yildiz.ce.dap.acm.util.exception.CacheCanNotBeFoundException;
import tr.edu.yildiz.ce.dap.acm.util.exception.CacheConfigurationFaultException;
import tr.edu.yildiz.ce.dap.acm.util.exception.CanNotGenerateIDException;
import tr.edu.yildiz.ce.dap.appagent.exception.AgentDeploymentException;
import tr.edu.yildiz.ce.dap.appagent.model.Component;
import tr.edu.yildiz.ce.dap.appagent.service.DownloadService;

public class CacheManagerServiceImpl implements CacheManagerService {


	public void resetCache() {
		File cacheFolder = new File(CacheProperties.getInstance().getCacheFolder());
		File[] files = cacheFolder.listFiles();
		for (File file : files) {
			boolean deleted = file.delete();
			if (!deleted)
				System.out.println("File can't be deleted : " + file.getAbsolutePath());
		}
	}

	public void addComponent(Component component)
			throws AgentDeploymentException,AlreadyCachedException, CacheConfigurationFaultException,CanNotGenerateIDException {
		Cache cache = getCache();
		CachedComponent cComponent = new CachedComponent();
		cComponent.setComponentId(generateId(component));
		if (cache.getCachedComponent().contains(cComponent))
			throw new AlreadyCachedException();
		DownloadService.download(component.getUrl(), getCachedFilePath(cComponent));
		cache.getCachedComponent().add(cComponent);
		updateFile(cache);
	}

	public Cache getCache() throws CacheConfigurationFaultException {
		Cache cache = null;
		try {
			if (!CacheProperties.getInstance().getCacheFile().exists())
				return new Cache();
			else 
				cache = CacheXmlConverter.convertToObject(CacheProperties.getInstance().getCacheFile());
		} catch (JAXBException e) {
			throw new CacheConfigurationFaultException(e);
		}
		return cache;
	}

	private static String convertToHex(byte[] data) {
		StringBuffer buf = new StringBuffer();
		for (int i = 0; i < data.length; i++) {
			int halfbyte = (data[i] >>> 4) & 0x0F;
			int two_halfs = 0;
			do {
				if ((0 <= halfbyte) && (halfbyte <= 9))
					buf.append((char) ('0' + halfbyte));
				else
					buf.append((char) ('a' + (halfbyte - 10)));
				halfbyte = data[i] & 0x0F;
			} while (two_halfs++ < 1);
		}
		return buf.toString();
	}

	public static String convertMD5(String text) throws NoSuchAlgorithmException,
			UnsupportedEncodingException {
		MessageDigest md;
		md = MessageDigest.getInstance("MD5");
		byte[] md5hash = new byte[32];
		md.update(text.getBytes("UTF-8"), 0, text.length());
		md5hash = md.digest();
		return convertToHex(md5hash);
	}

	public String generateId(Component component) throws CanNotGenerateIDException{
		String str = component.getName() + component.getVersion();	
		try {
			return convertMD5(str);
		} catch (Exception e) {
			throw new CanNotGenerateIDException(e);
		} 
	}
	
	private void updateFile(Cache cache) throws CacheConfigurationFaultException{
		try  {
			FileWriter writer = new FileWriter(CacheProperties.getInstance().getCacheFile());
			String xml = CacheXmlConverter.convertToXML(cache);
			writer.write(xml);
			writer.close();
		}catch (Exception e) {
			throw new CacheConfigurationFaultException(e);
		}
	}

	public void removeComponent(Component component)
			throws CacheCanNotBeFoundException,
			CacheConfigurationFaultException,CanNotGenerateIDException {
		Cache cache = getCache();
		CachedComponent cComponent = isComponentCached(component, cache);
		File componentFile = new File(getCachedFilePath(cComponent));
		if (componentFile.exists())
			componentFile.delete();
		cache.getCachedComponent().remove(cComponent);
		updateFile(cache);
	}

	private String getCachedFilePath(CachedComponent cComponent) {
		return CacheProperties.getInstance().getCacheFolder() + "/" +  cComponent.getComponentId();
	}

	private CachedComponent isComponentCached(Component component, Cache cache)
			throws CanNotGenerateIDException, CacheCanNotBeFoundException {
		CachedComponent cComponent = new CachedComponent();
		cComponent.setComponentId(generateId(component));
		if (!cache.getCachedComponent().contains(cComponent))
			throw new CacheCanNotBeFoundException();
		return cComponent;
	}

	public String getUrl(Component component)
			throws CacheCanNotBeFoundException,
			CacheConfigurationFaultException,CanNotGenerateIDException {
		Cache cache = getCache();
		CachedComponent cComponent = isComponentCached(component, cache);
		return "http://" + CacheProperties.getInstance().getHostname() + ":" + CacheProperties.getInstance().getPort() + Activator.CONTEXT_PATH + "?id=" + cComponent.getComponentId();
	}
	
	public void writeCachedComponent(PrintWriter pw, CachedComponent cComponent) throws IOException {
		File file = new File(getCachedFilePath(cComponent));
		FileInputStream is = new FileInputStream(file);
		int c = 0;
		while((c = is.read()) != -1){
			pw.write(c);
		}
		is.close();
	}

	public String generateUrl(Component component)
			throws CacheConfigurationFaultException, CanNotGenerateIDException {
		String hashedString = generateId(component);
		return "http://" + CacheProperties.getInstance().getHostname() + ":" + CacheProperties.getInstance().getPort() + Activator.CONTEXT_PATH + "?id=" + hashedString;
	}

}
